<template>
  <BasicModal
    @register="registerModal"
    ref="modalRef"
    :title="title"
    :width="1200"
    :maskClosable="false"
    :defaultFullscreen="true"
    :confirmLoading="confirmLoading"
    v-bind="$attrs"
    @cancel="onCancel"
  >
    <template #footer>
      <a-button @click="onCancel">关闭</a-button>
      <a-button type="primary" :loading="confirmLoading" preIcon="ant-design:save" @click="onSubmit">保存</a-button>
      <div style="display: inline-block; text-align: left; position: absolute; left: 0">
        <a-select
          v-model:value="aiTestTable"
          onUpdateValue="($event) => (aiTestTable = $event)"
          :getPopupContainer="(n) => n.parentElement"
          placeholder="请选择测试的表类型"
          style="width: 250px; margin: 0 10px 0 20px"
        >
          <a-select-option v-for="(item, index) in aiTableList" :key="index" :value="item.name">{{
            item.title + '(' + item.name + ')'
          }}</a-select-option>
        </a-select>
        <a-button type="primary" ghost @click="initVirtualData">生成数据>></a-button>
      </div>
    </template>

    <template #default>
      <a-spin class="p-2" :spinning="confirmLoading">
        <template #default>
          <BasicForm @register="registerForm">
            <template #extConfigButton>
              <div style="flex: 1; text-align: right">
                <a-button preIcon="ant-design:setting" @click="onOpenExtConfig">扩展配置</a-button>
              </div>
            </template>
          </BasicForm>
          <a-spin :spinning="tableLoading || hideTabs">
            <template #default>
              <a-tabs v-model:activeKey="activeKey" animated @change="onTabsChange">
                <template #default>
                  <a-tab-pane tab="数据库属性" key="dbTable" :forceRender="true">
                    <DBAttributeTable
                      ref="dbTable"
                      :action-button="actionButton"
                      @added="onTableAdded"
                      @removed="onTableRemoved"
                      @dragged="onTableDragged"
                      @inserted="onTableInserted"
                      @syncDbType="onTableSyncDbType"
                    />
                  </a-tab-pane>

                  <a-tab-pane tab="页面属性" key="pageTable" forceRender>
                    <PageAttributeTable ref="pageTable" />
                  </a-tab-pane>

                  <a-tab-pane tab="校验字段" key="checkTable" forceRender>
                    <CheckDictTable ref="checkTable" />
                  </a-tab-pane>

                  <a-tab-pane tab="外键" key="fkTable" forceRender>
                    <ForeignKeyTable ref="fkTable" :actionButton="actionButton" />
                  </a-tab-pane>

                  <a-tab-pane tab="索引" key="idxTable" forceRender>
                    <IndexTable ref="idxTable" :actionButton="actionButton" />
                  </a-tab-pane>

                  <a-tab-pane key="queryTable" forceRender>
                    <template #tab>
                      <span>
                        个性查询配置
                        <a-tooltip placement="top" title="允许自定义，查询表单字段控件类型！" arrow-point-at-center>
                          <!-- <question-circle-outlined /> -->
                          <Icon icon="bx:help-circle" />
                        </a-tooltip>
                      </span>
                    </template>
                    <QueryTable ref="queryTable" :actionButton="actionButton" />
                  </a-tab-pane>
                </template>
              </a-tabs>
            </template>
          </a-spin>
        </template>
      </a-spin>
      <ExtendConfigModal @register="registerExtendConfigModal" :parentForm="formAction" @ok="onExtConfigOk" />
    </template>
  </BasicModal>
</template>
<script lang="ts">
  import { defineComponent, ref, computed, provide, reactive, nextTick, toRaw, onMounted } from 'vue';
  import { Icon } from '/@/components/Icon';
  import { BasicModal, useModalInner, useModal } from '/@/components/Modal';
  import { BasicForm, useForm } from '/@/components/Form/index';
  import { useMessage } from '/@/hooks/web/useMessage';
  import { simpleDebounce } from '/@/utils/common/compUtils';
  import { sleep } from '/@/utils';
  import { buildUUID } from '/@/utils/uuid';
  import { useInitialData, ExtConfigDefaultJson, useTreeNeedFields, VALIDATE_FAILED } from '../cgform.data';
  import { defHttp } from '/@/utils/http/axios';
  import { getRefPromise } from '../use/useAutoForm';
  import { useFormSchemas } from '../use/useSchemas';
  import { useOnlineTest } from '../use/useOnlineTest';
  import DBAttributeTable from './tables/DBAttributeTable.vue';
  import PageAttributeTable from './tables/PageAttributeTable.vue';
  import CheckDictTable from './tables/CheckDictTable.vue';
  import ForeignKeyTable from './tables/ForeignKeyTable.vue';
  import IndexTable from './tables/IndexTable.vue';
  import QueryTable from './tables/QueryTable.vue';
  import ExtendConfigModal from '../components/ExtendConfigModal.vue';

  const formApi = {
    doQueryField: (headId, params) => {
      return defHttp.get({ url: '/api-online/online/cgform/field/listByHeadId', params: Object.assign({ headId }, params) });
    },
    doQueryIndexes: (headId, params) => {
      return defHttp.get({ url: '/api-online/online/cgform/index/listByHeadId', params: Object.assign({ headId }, params) });
    },
    doSaveOrUpdate: (params, isUpdate) => {
      if (isUpdate) {
        return defHttp.put({ url: '/api-online/online/cgform/api/editAll', params });
      } else {
        return defHttp.post({ url: '/api-online/online/cgform/api/addAll', params });
      }
    },
    editHead: (params) => {
      return defHttp.put({ url: '/api-online/online/cgform/head/edit', params });
    },
  };

  export default defineComponent({
    name: 'CgformModal',
    components: {
      BasicModal,
      BasicForm,
      DBAttributeTable,
      PageAttributeTable,
      CheckDictTable,
      ForeignKeyTable,
      IndexTable,
      QueryTable,
      ExtendConfigModal,
      Icon,
    },
    props: {
      actionButton: {
        type: Boolean,
        default: true,
        required: false,
      },
    },
    emits: ['success', 'register'],
    setup(props, { emit }) {
      const { createMessage: $message } = useMessage();
      const modalRef = ref();
      const isUpdate = ref(false);
      let model = {};
      const title = computed(() => (isUpdate.value ? '编辑' : '新增'));
      const confirmLoading = ref(true);
      const tableLoading = ref(false);
      const activeKey = ref('dbTable');
      const hideTabs = ref(true);
      const tables = {
        dbTable: ref(),
        pageTable: ref(),
        checkTable: ref(),
        fkTable: ref(),
        idxTable: ref(),
        queryTable: ref(),
      };
      const fullScreenRef = computed(() => {
        return modalRef?.value?.fullScreenRef != null ? modalRef.value.fullScreenRef : false;
      });
      provide('tables', tables);
      provide('fullScreenRef', fullScreenRef);
      const { formSchemas } = useFormSchemas(props, {
        onTableTypeChange,
        onIsTreeChange,
        ifShowOfSubTableStr: () => showSubTableStr,
      });
      const [registerForm, formAction] = useForm({
        schemas: formSchemas,
        showActionButtonGroup: false,
        labelAlign: 'right',
      });
      const { resetFields, setFieldsValue, validate } = formAction;
      const [registerModal, { closeModal }] = useModalInner((data) => {
        isUpdate.value = data?.isUpdate != null ? data.isUpdate : false;
        if (isUpdate.value) {
          edit(data == null ? void 0 : data.record);
        } else {
          add();
        }
      });
      const oldTableName = ref('');
      let extConfigJson = reactive({});
      const syncAllTableNowDebounce = simpleDebounce(() => syncAllTableNowPromise(), 150);
      let fieldTempIds = [];
      let showSubTableStr = false;
      let treeFieldAdded = false;
      let treeFieldIds = [];
      const { aiTestMode, aiTestTable, aiTableList, initVirtualData, tableJsonGetHelper, refreshCacheTableName } = useOnlineTest();
      function add() {
        edit({});
      }
      async function edit(record) {
        confirmLoading.value = false;
        activeKey.value = 'dbTable';
        await resetFields();
        model = Object.assign({}, record);
        initialAllShowItem(model);
        tableJsonGetHelper(model);
        initialExtConfigJson(model);
        setFieldsValue(model);
        oldTableName.value = model.tableName;
        sleep(1, () => (hideTabs.value = false));
        if (isUpdate.value) {
          tables.dbTable?.value.setDataSource([]);
          await loadFields(model.id);
          await loadIndexes(model.id);
          getRefPromise(tables.pageTable).then(() => {
            tables.pageTable.value.changePageType(model.tableType == 3);
          });
        } else {
          let { initialData, tempIds } = useInitialData();
          await setAllTableData(initialData, true);
          fieldTempIds = tempIds;
        }
      }
      async function loadFields(headId) {
        tableLoading.value = true;
        try {
          let fields = await formApi.doQueryField(headId);
          tableLoading.value = false;
          await setAllTableData(fields);
        } finally {
          tableLoading.value = false;
        }
      }
      async function loadIndexes(headId) {
        let indexes = await formApi.doQueryIndexes(headId);
        tables.idxTable.value.setDataSource(indexes);
      }
      function initialExtConfigJson(record) {
        let parseJSON = {};
        if (record.extConfigJson) {
          try {
            parseJSON = JSON.parse(record.extConfigJson);
          } catch (e) {
            console.error('online扩展JSON转换失败：', e);
          }
        }
        extConfigJson = Object.assign({}, ExtConfigDefaultJson, parseJSON, {
          isDesForm: record.isDesForm || 'N',
          desFormCode: record.desFormCode || '',
        });
      }
      function initialAllShowItem(model2) {
        treeFieldAdded = model2.isTree == 'Y';
        showSubTableStr = model2.tableType === 2;
      }
      async function setAllTableData(data, insert) {
        const { dbTable, pageTable, checkTable, fkTable, queryTable } = tables;
        if (!dbTable.value) {
          await nextTick();
          await sleep(1);
        }
        dbTable.value.setDataSource(data, insert);
        setTimeout(() => {
          pageTable.value.setDataSource(data, insert);
          checkTable.value.setDataSource(data, insert);
          fkTable.value.setDataSource(data, insert);
          queryTable.value.setDataSource(data, insert);
        }, 10);
      }
      function onTabsChange(activeKey2) {
        if (['pageTable', 'checkTable', 'fkTable', 'idxTable', 'queryTable'].indexOf(activeKey2) !== -1) {
          const dbTable = tables.dbTable;
          const table = tables[activeKey2];
          dbTable.value.tableRef.resetScrollTop();
          table.value.syncTable(dbTable);
        }
      }
      function onTableTypeChange(value) {
        if (value === 1) {
          setFieldsValue({ themeTemplate: 'normal' });
        }
        tables.pageTable.value.changePageType(value == 3);
      }
      function onIsTreeChange(value) {
        value === 'Y' ? addTreeNeedField() : deleteTreeNeedField();
      }
      function syncAllTableNow() {
        syncAllTableNowDebounce();
      }
      async function syncAllTableNowPromise() {
        let { dbTable, pageTable, checkTable, fkTable, queryTable } = tables;
        await pageTable.value.syncTable(dbTable);
        await checkTable.value.syncTable(dbTable);
        await fkTable.value.syncTable(dbTable);
        await queryTable.value.syncTable(dbTable);
      }
      function onTableAdded() {
        syncAllTableNow();
      }
      function onTableRemoved() {
        syncAllTableNow();
      }
      function onTableDragged(event) {
        let { oldIndex, newIndex } = event;
        syncAllOrderNumNow(oldIndex, newIndex);
      }
      async function onTableInserted(event) {
        let { insertIndex, row } = event;
        let { pageTable, checkTable, fkTable, queryTable } = tables;
        pageTable.value.tableRef.insertRows(row, insertIndex);
        checkTable.value.tableRef.insertRows(row, insertIndex);
        fkTable.value.tableRef.insertRows(row, insertIndex);
        queryTable.value.tableRef.insertRows(row, insertIndex);
      }
      function syncAllOrderNumNow(oldIndex, newIndex) {
        let { pageTable, checkTable, fkTable, queryTable } = tables;
        pageTable.value.tableRef.rowResort(oldIndex, newIndex);
        checkTable.value.tableRef.rowResort(oldIndex, newIndex);
        fkTable.value.tableRef.rowResort(oldIndex, newIndex);
        queryTable.value.tableRef.rowResort(oldIndex, newIndex);
      }
      function onTableSyncDbType(event) {
        tables.pageTable.value.syncFieldShowType(event.row);
      }
      function onTableQuery(id) {
        tables.pageTable.value.enableQuery(id);
      }
      function addTreeNeedField() {
        if (!treeFieldAdded) {
          let { dbTable, pageTable, checkTable } = tables;
          let treeFields = useTreeNeedFields();
          treeFields = treeFields.filter((item) => {
            let nameList = dbTable.value.tableRef.getTableData().map((o) => o.dbFieldName);
            return !nameList.includes(item.dbFieldName);
          });
          treeFieldIds = [];
          treeFields.forEach((newData) => {
            let uuidTemp = buildUUID() + '__tempId';
            treeFieldIds.push(uuidTemp);
            newData.id = uuidTemp;
          });
          dbTable.value.tableRef.addRows(treeFields, { setActive: false });
          pageTable.value.tableRef.addRows(treeFields, { setActive: false });
          checkTable.value.tableRef.addRows(treeFields, { setActive: false });
          nextTick(() => syncAllTableNow());
          treeFieldAdded = true;
        }
        nextTick(() => {
          formAction.setFieldsValue({
            treeIdField: 'has_child',
            treeParentIdField: 'pid',
          });
        });
      }
      function deleteTreeNeedField() {
        if (treeFieldIds && treeFieldIds.length > 0) {
          let { dbTable } = tables;
          dbTable.value.tableDeleteLines(treeFieldIds);
          treeFieldIds = [];
          treeFieldAdded = false;
        }
      }
      function validateAll() {
        let options = {};
        return new Promise((resolve, reject) => {
          validate().then(
            (values) => resolve({ values }),
            () => reject(VALIDATE_FAILED)
          );
        })
          .then((result) => {
            Object.assign(options, result);
            return validateTableFields();
          })
          .then((allTableData) => {
            Object.assign(options, allTableData);
            let formData = classifyIntoFormData(options);
            return validateForeignKey(formData);
          })
          .catch((e) => {
            if (e === VALIDATE_FAILED || (e == null ? void 0 : e.code) === VALIDATE_FAILED) {
              $message.warning('校验未通过');
            } else {
              console.error(e);
            }
            return Promise.reject(null);
          });
      }
      function validateTableFields() {
        return new Promise(async (resolve, reject) => {
          let tableKeys = Object.keys(tables);
          let allTableData = {};
          for (let i = 0; i < tableKeys.length; i++) {
            let key = tableKeys[i];
            let table = tables[key];
            try {
              allTableData[key] = await table.value.validateData(key);
            } catch (e) {
              if (e.code === VALIDATE_FAILED) {
                activeKey.value = e.activeKey;
              } else {
                console.error(e);
              }
              reject(e);
              return;
            }
          }
          resolve(allTableData);
        });
      }
      function classifyIntoFormData(options) {
        let formData = {
          head: {},
          fields: [],
          indexs: [],
          deleteFieldIds: [],
          deleteIndexIds: [],
        };
        formData.head = Object.assign(model, options.values);
        formData.head.isDesForm = extConfigJson.isDesForm;
        formData.head.desFormCode = extConfigJson.desFormCode;
        delete extConfigJson.isDesForm;
        delete extConfigJson.desFormCode;
        formData.head.extConfigJson = JSON.stringify(extConfigJson);
        options.dbTable.tableData.forEach((item, index) => {
          let rowId = item.id;
          let fields = Object.assign({}, item);
          let pageTable = options.pageTable.tableData[index];
          fields = Object.assign(pageTable, fields);
          let checkTable = options.checkTable.tableData[index];
          fields = Object.assign(checkTable, fields);
          let fkTable = options.fkTable.tableData[index];
          fields = Object.assign(fkTable, fields);
          let queryTable = options.queryTable.tableData[index];
          fields = Object.assign(queryTable, fields);
          if (rowId == null || rowId === '') {
            delete fields.id;
          } else {
            fields.id = rowId;
          }
          let tempIds = [].concat(fieldTempIds, treeFieldIds);
          if (tempIds.includes(fields.id)) {
            delete fields.id;
          }
          formData.fields.push(fields);
        });
        formData.deleteFieldIds = options.dbTable.deleteIds;
        formData.indexs = options.idxTable.tableData;
        formData.deleteIndexIds = options.idxTable.deleteIds;
        return formData;
      }
      function validateForeignKey(formData) {
        return new Promise((resolve, reject) => {
          let fields = formData.fields;
          let saved = true;
          if (fields && fields.length > 0) {
            let hasForeignKey = 0;
            for (let i = 0; i < fields.length; i++) {
              if (fields[i].mainField || fields[i].mainTable) {
                hasForeignKey += 1;
              }
              if (hasForeignKey > 1) {
                saved = false;
                break;
              }
            }
          }
          if (saved) {
            resolve(formData);
          } else {
            reject({
              code: -1,
              msg: '外键只允许配置一个!',
              error: VALIDATE_FAILED,
            });
          }
        });
      }
      function onSubmit() {
        confirmLoading.value = true;
        validateAll()
          .then(
            async (formData) => {
              var head;
              console.log(formData);
              if (formData.fields && formData.fields.length > 0) {
                for (let field of formData.fields) {
                  field.dbFieldName = field.dbFieldName.toLowerCase().trim();
                }
              }
              if ((head = formData.head) == null ? void 0 : head.tableName) {
                formData.head.tableName = formData.head.tableName.toLowerCase().trim();
              }
              await formApi.doSaveOrUpdate(formData, isUpdate.value);
              refreshCacheTableName(oldTableName.value, formData.head['tableName']);
              emit('success');
              sleep(1, () => onCancel());
            },
            (e) => {
              console.error(e);
            }
          )
          .finally(() => {
            confirmLoading.value = false;
          });
      }
      const [registerExtendConfigModal, extendConfigModal] = useModal();
      async function onExtConfigOk(values) {
        extConfigJson = values;
        if (isUpdate.value == true) {
          let json = toRaw(extConfigJson);
          const params = {
            id: model.id,
            extConfigJson: JSON.stringify(json),
          };
          await formApi.editHead(params);
          emit('success');
        }
      }
      function onOpenExtConfig() {
        extendConfigModal.openModal(true, {
          extConfigJson,
        });
      }
      function onCancel() {
        hideTabs.value = true;
        sleep(1, () => closeModal());
      }

      return Object.assign({}, tables, {
        modalRef,
        title,
        confirmLoading,
        tableLoading,
        activeKey,
        onCancel,
        extConfigJson,
        formAction,
        hideTabs,
        onSubmit,
        onTabsChange,
        onTableAdded,
        onTableRemoved,
        onTableDragged,
        onTableInserted,
        onTableSyncDbType,
        onTableQuery,
        onOpenExtConfig,
        onExtConfigOk,
        registerForm,
        registerModal,
        registerExtendConfigModal,
        aiTestMode,
        aiTestTable,
        aiTableList,
        initVirtualData,
      });
    },
  });
</script>
<style scoped></style>
